package com.demo2do.fms.scheduler.service.impl;

import java.util.Date;
import java.util.List;

import com.demo2do.core.entity.exception.BusinessException;
import com.demo2do.core.persistence.GenericDaoSupport;
import com.demo2do.fms.entity.vehicle.tractor.Tractor;
import com.demo2do.fms.entity.vehicle.tractor.TractorEvent;
import com.demo2do.fms.entity.vehicle.tractor.TractorEventType;
import com.demo2do.fms.entity.vehicle.trailer.Trailer;
import com.demo2do.fms.entity.vehicle.trailer.TrailerEvent;
import com.demo2do.fms.entity.vehicle.trailer.TrailerEventType;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.demo2do.core.persistence.annotation.Dao;
import com.demo2do.fms.entity.driver.Driver;
import com.demo2do.fms.entity.driver.DriverAuditStatus;
import com.demo2do.fms.entity.vehicle.source.LockStatus;
import com.demo2do.fms.entity.vehicle.source.LockType;
import com.demo2do.fms.entity.vehicle.source.Vehicle;
import com.demo2do.fms.entity.vehicle.source.VehicleGpsInfo;
import com.demo2do.fms.scheduler.Constants;
import com.demo2do.fms.scheduler.dao.fms.VehicleLockDao;
import com.demo2do.fms.scheduler.dao.fms.VehicleLockDaoSupport;
import com.demo2do.fms.scheduler.entity.RemoteTransMeans;
import com.demo2do.fms.scheduler.entity.VehicleSource;
import com.demo2do.fms.scheduler.entity.VehicleSourceLockRecord;
import com.demo2do.fms.scheduler.service.VehicleLockWriter;

/**
 *
 * @author Ark
 *
 */
@Service("vehicleLockWriter")
public class VehicleLockWriterImpl implements VehicleLockWriter {
	
	private static final Logger logger = LoggerFactory.getLogger(VehicleLockWriterImpl.class);
	
	@Dao
	private VehicleLockDao vehicleLockDao;
	
	@Autowired
	private VehicleLockDaoSupport vehicleLockDaoSupport;

	@Autowired
	private GenericDaoSupport genericDaoSupport;
	
	/* (non-Javadoc)
	 * @see com.demo2do.fms.scheduler.service.VehicleLockService#lockVehicleNoDriver(java.util.List)
	 */
	public void lockVehicleNoDriver(List<Vehicle> vehicles) {
		
		logger.trace("#lockVehicleNoDriver() - lock no driver vehicle start...");
		
		// 0. check vehicles
		if (CollectionUtils.isEmpty(vehicles)) {
			logger.trace("#lockVehicleNoDriver() - vehicles is empty");
			return;
		}
		
		for (Vehicle vehicle : vehicles) {
			
			// 1. load vehicleSource by code && check lock status
			String code = vehicle.getCode();
			VehicleSource vehicleSource = vehicleLockDao.loadVehicleSource(code);
			logger.trace("#lockVehicleNoDriver() - vehicle code is [{}]", code);
			
			if (vehicleSource == null || vehicleSource.isLock()) {
				logger.trace("#lockVehicleNoDriver() - vehicle_source[code={}] not exist or locked", code);
				continue;
			}
			
			// 2. load remoteTransMeans by code && check avaliable
			RemoteTransMeans transMeans = vehicleLockDao.loadRemoteTransMeans(code);
			if (transMeans == null || !transMeans.isAvaliable()) {
				logger.trace("#lockVehicleNoDriver() - remote_trans_means[code={}] not exist or not avaliable", code);
				continue;
			}
			
			// 3. judge remote_trans_means whether is_locked
			LockType lockType = LockType.SHIPMENT_EXCEPTION;
			if (transMeans.isLock()) {
				if (StringUtils.isNotBlank(transMeans.getLockType())) {
					lockType = LockType.codeOf(transMeans.getLockType());
				}
			}
			
			// 4. lock vehicleSource 
			String lockDescription = lockType.getDescription();
			vehicleLockDao.updateVehicleSource(vehicleSource.doLock(lockDescription, lockType));
			
			// 5. save lock vehicle record
			String description = "该车没有司机与之关联 - " + lockDescription;
			VehicleSourceLockRecord record = new VehicleSourceLockRecord(vehicleSource.getId(), LockStatus.LOCKED.ordinal(), description);
			vehicleLockDaoSupport.save(record);
			
			// 6. lock remoteTransMeans
			if (!transMeans.isLock()) {
				vehicleLockDao.updateRemoteTransMeans(transMeans.lock(lockType, Constants.MDM_LOCK_USERID));
			}
			
			logger.warn("#lockVehicleNoDriver() - vehicle_source[code={}] already locked", code);
			
		}
		
		logger.trace("#lockVehicleNoDriver() - lock no driver vehicle end...");
		
	}
	
	/* (non-Javadoc)
	 * @see com.demo2do.fms.scheduler.service.VehicleLockService#lockVehicleByGps(java.util.List)
	 */
	public void lockVehicleByGps(List<Long> vehicleIds) {

		logger.trace("#lockVehicleByGps() - lock vehicle by gps start...");
		
		if (CollectionUtils.isEmpty(vehicleIds)) {
			logger.trace("#lockVehicleByGps() - vehicleIds is empty");
			return;
		}
		
		for (Long vehicleId : vehicleIds) {
			
			logger.trace("#lockVehicleByGps() - remote_trans_means id is [{}]", vehicleId);
			
			// 1. load remoteTransMeans & check avaliable
			RemoteTransMeans transMeans = vehicleLockDao.loadRemoteTransMeans(vehicleId);
			if (transMeans == null || !transMeans.isAvaliable()) {
				logger.trace("#lockVehicleByGps() - remote_trans_means[id={}] is null or remote_trans_means is not avaliable", vehicleId);
				continue;
			}
			
			// 2. load vehicleSource by code && check lock status
			String code = transMeans.getCode();
			VehicleSource vehicleSource = vehicleLockDao.loadVehicleSource(code);
			if (vehicleSource == null || vehicleSource.isLock()) {
				logger.trace("#lockVehicleByGps() - vehicle_source[code={}] is null or vehicle_source is locked", code);
				continue;
			}
			
			// 3. judge remote_trans_means whether is_locked
			LockType lockType = LockType.SHIPMENT_EXCEPTION;
			if (transMeans.isLock()) {
				if (StringUtils.isNotBlank(transMeans.getLockType())) {
					lockType = LockType.codeOf(transMeans.getLockType());
				}
			}
			
			// 4. lock vehicleSource
			String lockDescription = lockType.getDescription();
			vehicleLockDao.updateVehicleSource(vehicleSource.doLock(lockDescription, lockType));
			
			// 5. save vehicle lock record
			String description = "该车7天内没有GPS信息 - " + lockDescription;
			VehicleSourceLockRecord record = new VehicleSourceLockRecord(vehicleSource.getId(), LockStatus.LOCKED.ordinal(), description);
			vehicleLockDaoSupport.save(record);
			
			// 6. lock remoteTransMeans
			if (!transMeans.isLock()) {
				vehicleLockDao.updateRemoteTransMeans(transMeans.lock(lockType, Constants.MDM_LOCK_USERID));
			}
			
			logger.warn("#lockVehicleByGps() - vehicle_source[code={}] already locked", code);
			
		}
		
		logger.trace("#lockVehicleByGps() - lock vehicle by gps end...");
		
	}
	
	/* (non-Javadoc)
	 * @see com.demo2do.fms.scheduler.service.VehicleLockService#unlockVehicleByGps(java.util.List, java.util.Date)
	 */
	public void unlockVehicleByGps(List<VehicleGpsInfo> vehicleGpsInfos, Date twoHoursBefore) {
		
		logger.trace("#unlockVehicleByGps() - unlock vehicle by gps start...");
		
		// 0. check gpsInfos
		if (CollectionUtils.isEmpty(vehicleGpsInfos)) {
			logger.trace("#unlockVehicleByGps() - vehicleGpsInfos is empty");
			return;
		}
		
		for (VehicleGpsInfo vehicleGpsInfo : vehicleGpsInfos) {
			
			logger.trace("#unlockVehicleByGps() - remote_trans_means id is [{}]", vehicleGpsInfo.getVid());
			
			// 1. load remoteTransMeans & check avaliable & lockType equals 01
			RemoteTransMeans transMeans = vehicleLockDao.loadRemoteTransMeans(vehicleGpsInfo.getVid());
			if (transMeans == null || !transMeans.isAvaliable() || !transMeans.isShipmentException()) {
				logger.trace("#unlockVehicleByGps() - remote_trans_means[id={}] is null or remote_trans_means is not avaliable or is not shipment exception", vehicleGpsInfo.getVid());
				continue;
			}
					
			// 2. load vehicleSource & check lock satus
			String code = transMeans.getCode();
			VehicleSource vehicleSource = vehicleLockDao.loadVehicleSource(code);
			if (vehicleSource == null || !vehicleSource.isLock()) {
				logger.trace("#unlockVehicleByGps() - vehicle_source[code={}] is null or vehicle_source is unlocked", code);
				continue;
			}
					
			// 3.1 valid vehicleSource whether can unlock
			String errorMessage = this.validUnlock(vehicleSource, twoHoursBefore);
            if(StringUtils.isNotBlank(errorMessage)) {
                logger.trace("#unlockVehicleByGps() - vehicle[code={}] unlock vehicle failed with reason[{}]", code, errorMessage);
                continue;
            }
					
			// 3.2 unlock vehicleSource
            vehicleLockDao.updateVehicleSource(vehicleSource.unlock());
					
			// 3.3 save vehicle lock record
            String description = "自动扫描GPS解锁";
            VehicleSourceLockRecord record = new VehicleSourceLockRecord(vehicleSource.getId(), LockStatus.UNLOCKED.ordinal(), description);
            vehicleLockDaoSupport.save(record);
					
			// 4. unlock remoteTransMeans
            if(transMeans.isLock()) {
            	vehicleLockDao.updateRemoteTransMeans(transMeans.unlock(Constants.MDM_LOCK_USERID));
            }
            
            logger.trace("#unlockVehicleByGps() - vehicle_source[code={}] already unlocked", code);
            
		}
		
		logger.trace("#unlockVehicleByGps() - unlock vehicle by gps end...");
		
	}
	
	/**
	 * @param vehicleSource
	 * @param twoHoursBefore
	 * @return
	 */
	private String validUnlock(VehicleSource vehicleSource, Date twoHoursBefore) {
		
		if(vehicleSource.isUnsynchro()) {
            return "车辆暂未同步";
        }
        
        if(StringUtils.isEmpty(vehicleSource.getCode())) {
            return "车辆暂未同步，数据异常";
        }
        
        Vehicle vehicle = vehicleLockDao.loadVehicle(vehicleSource.getCode());
        List<Driver> drivers = vehicleLockDao.listDriver(vehicle.getId(), DriverAuditStatus.QUALIFIED.name());
        if (CollectionUtils.isEmpty(drivers)) {
            return "车辆未和司机绑定";
        }
        
        return null;
		
	}

	/**
	 * @param vehicleSource
	 * @return
	 */
	private String validUnlock(VehicleSource vehicleSource) {

		if(vehicleSource.isUnsynchro()) {
			return "车辆暂未同步";
		}

		if(StringUtils.isEmpty(vehicleSource.getCode())) {
			return "车辆暂未同步，数据异常";
		}

		Vehicle vehicle = vehicleLockDao.loadVehicle(vehicleSource.getCode());

		if(vehicle == null){
			return "该车辆不存在";
		}

		List<Driver> drivers = vehicleLockDao.listDriver(vehicle.getId(), DriverAuditStatus.QUALIFIED.name());
		if (CollectionUtils.isEmpty(drivers)) {
			return "车辆未和司机绑定";
		}

		return null;

	}
	
	/* (non-Javadoc)
	 * @see com.demo2do.fms.scheduler.service.VehicleLockService#syncVehicleByMdm()
	 */
	public void syncVehicleByMdm() {
		
		logger.trace("#syncVehicleByMdm() - sync mdm vehicle lock status start...");
		
    	Date twoHourBefore = DateUtils.addHours(new Date(), -2);
		
		// 1. list mdm locked or unlocked vehicle in two hour
    	List<RemoteTransMeans> lockRemoteTransMeans = vehicleLockDao.listRemoteTransMeans(twoHourBefore);
    	logger.trace("#syncVehicleByMdm() - unlock mdm vehicle size=[{}]", lockRemoteTransMeans.size());
    	if (CollectionUtils.isEmpty(lockRemoteTransMeans)) {
    		logger.trace("#syncVehicleByMdm() - mdm no change lock status vehicle in two hour");
    		return;
    	}
		
		// 2. get fms vehicle by mdm and check status
    	for (RemoteTransMeans transMeans : lockRemoteTransMeans) {
    		
    		logger.trace("#unlockVehicleByGps() - remote_trans_means code is [{}]", transMeans.getCode());
    		
    		// 2.1 check transMeans whether avaliable
    		if (!transMeans.isAvaliable()) {
    			logger.trace("#syncVehicleByMdm() - remote_trans_means is not avaliable");
    			continue;
    		}
    		
    		// 2.2 get fms vehicle by mdm and check status
    		VehicleSource vehicleSource = vehicleLockDao.loadVehicleSource(transMeans.getCode());
    		if (vehicleSource != null) {
    		    
    		    this.syncVehicle(transMeans, vehicleSource);
    		    
    		} else {
    		    
    		    VehicleSource deletedVehicle = vehicleLockDao.loadDeletedVehicleSource(transMeans.getCode());
    		    if (deletedVehicle == null) {
    		        logger.trace("#lockVehicleByMdm() - fms vehicle[code={}] no exists", transMeans.getCode());
    		        continue;
    		    }

    		    this.lockVehicleByDeleted(transMeans, deletedVehicle);
    		    
    		}
    		
    	}
    	
    	logger.trace("#syncVehicleByMdm() - sync mdm vehicle lock status end...");
		
	}

	/**
	 * @param vehicleSources
	 * @return void
	 * @description
	 * @params [vids1]
	 * @author LZJ
	 * @date 2020/12/1 16:04
	 */
	public void lockVehicleByVehicleSource(List<com.demo2do.fms.entity.vehicle.source.VehicleSource> vehicleSources) {

		logger.trace("#lockVehicleByVehicleSource() - lock vehicle  start...");

		if(vehicleSources.isEmpty()){
			throw new BusinessException("do not lock any vehicle!");
		}

		for(com.demo2do.fms.entity.vehicle.source.VehicleSource vehicleSource : vehicleSources){
			//取其中的牵引车的vehicle code 执行锁车操作

			Vehicle vehicle = vehicleLockDao.loadVehicle(vehicleSource.getCode());

			// 1. load remoteTransMeans & check avaliable
			RemoteTransMeans transMeans = vehicleLockDao.loadRemoteTransMeans(vehicle.getId());
			if (transMeans == null || !transMeans.isAvaliable()) {
				logger.trace("#lockVehicleByVehicleSource() - remote_trans_means[id={}] is null or remote_trans_means is not avaliable", vehicle.getId());
				continue;
			}

			// 2. load vehicleSource by code && check lock status

			if (vehicleSource == null || vehicleSource.isLock()) {
				logger.trace("#lockVehicleByVehicleSource() - vehicle_source[code={}] is null or vehicle_source is locked");
				continue;
			}

			// 3. judge remote_trans_means whether is_locked
			LockType lockType = LockType.SHIPMENT_EXCEPTION;
			if (transMeans.isLock()) {
				if (StringUtils.isNotBlank(transMeans.getLockType())) {
					lockType = LockType.codeOf(transMeans.getLockType());
				}
			}

			// 4. lock vehicleSource
			String lockDescription = lockType.getDescription();
			genericDaoSupport.update(vehicleSource.doLock(lockDescription,lockType,new Date()));

			// 5. save vehicle lock record
			String description = "该车年检有效期已过期 - " + lockDescription;
			VehicleSourceLockRecord record = new VehicleSourceLockRecord(vehicleSource.getId(), LockStatus.LOCKED.ordinal(), description);
			vehicleLockDaoSupport.save(record);

			// 6. lock remoteTransMeans
			if (!transMeans.isLock()) {
				vehicleLockDao.updateRemoteTransMeans(transMeans.lock(lockType, Constants.MDM_LOCK_USERID));
			}

			logger.warn("#lockVehicleByVehicleSource() - vehicle_source[code={}] already locked");


			//锁车成功，同时将牵引车和挂车的审核状态改未“未审核”
			String tractorBrandCode = vehicleSource.getTractorBrandCode();
			String trailerBrandCode = vehicleSource.getTrailerBrandCode();

			Tractor tractor = vehicleLockDao.loadTractor(tractorBrandCode);
			Trailer trailer = vehicleLockDao.loadTrailer(trailerBrandCode);
			genericDaoSupport.update(tractor.makeStatusToAuditing());
			genericDaoSupport.update(trailer.makeStatusToAuditing());

			//“分属供方牵引车日志查询”与“分属供方挂车日志查询”产生对应的修改记录。
			genericDaoSupport.save(new TractorEvent(tractor, TractorEventType.CREATE));
			genericDaoSupport.save(new TrailerEvent(trailer, TrailerEventType.CREATE));

		}

		logger.trace("#lockVehicleByVehicleSource() - lock vehicle  end...");
	}

	/**
	 * @return void
	 * @description
	 * @params []
	 * @author LZJ
	 * @date 2020/12/1 17:02
	 **/
	public void unlockVehicle() {
		//3.0 load all VehicleSource
		List<VehicleSource> vehicleSources = vehicleLockDao.listLockVehicle();

		for(VehicleSource vehicleSource : vehicleSources){

			String code = vehicleSource.getCode();
			RemoteTransMeans transMeans = vehicleLockDao.loadRemoteTransMeans(code);

			// 3.1 valid vehicleSource whether can unlock
			String errorMessage = this.validUnlock(vehicleSource);
			if(StringUtils.isNotBlank(errorMessage)) {
				logger.trace("#unlockVehicleByGps() - vehicle[code={}] unlock vehicle failed with reason[{}]", code, errorMessage);
				continue;
			}

			// 3.2 unlock vehicleSource
			vehicleLockDao.updateVehicleSource(vehicleSource.unlock());

			// 3.3 save vehicle lock record
			String description = "自动扫描解锁";
			VehicleSourceLockRecord record = new VehicleSourceLockRecord(vehicleSource.getId(), LockStatus.UNLOCKED.ordinal(), description);
			vehicleLockDaoSupport.save(record);

			// 4. unlock remoteTransMeans
			if(transMeans.isLock()) {
				vehicleLockDao.updateRemoteTransMeans(transMeans.unlock(Constants.MDM_LOCK_USERID));
			}

			logger.trace("#unlockVehicleByGps() - vehicle_source[code={}] already unlocked", code);
		}

	}

	/**
	 * @param transMeans
	 * @param vehicleSource
	 */
	private void syncVehicle(RemoteTransMeans transMeans, VehicleSource vehicleSource) {
	    
	    LockType lockType = LockType.codeOf(transMeans.getLockType());
        String mark = StringUtils.isNotEmpty(transMeans.getMark()) ? transMeans.getMark() : "";
                
        // ************** 同步MDM已锁车记录(FMS未锁车) ***************
        if (transMeans.isLock() && !vehicleSource.isLock()) {
            
            // 3. update fms_vehicle info
            String fmsMark = null; 
            if (StringUtils.isNotBlank(mark)) {
                fmsMark = "MDM手动锁车 - " + mark;
            } else {
                fmsMark = "MDM锁车 - " + (lockType != null ? lockType.getDescription() : "");
            }
                
            vehicleLockDao.updateVehicleSource(vehicleSource.doLock(fmsMark, lockType));
            
            // 4. create lock record
            VehicleSourceLockRecord record = new VehicleSourceLockRecord(vehicleSource.getId(), LockStatus.LOCKED.ordinal(), fmsMark);
            vehicleLockDaoSupport.save(record);
            
            logger.warn("#syncVehicleByMdm() - vehicle_source[code={}] already locked", vehicleSource.getCode());
            
        }
        
        
        // ************** 同步MDM已解锁车记录(FMS已锁车) ***************
        if (!transMeans.isLock() && vehicleSource.isLock()) {
            
            // 3. update fms vehicle info
            vehicleLockDao.updateVehicleSource(vehicleSource.unlock());
            
            // 4. create lock record
            String description = null;
            if (StringUtils.isNotBlank(mark)) {
                description = "MDM手动解锁 - " + mark;
            } else {
                description = "MDM解锁";
            }
            
            VehicleSourceLockRecord record = new VehicleSourceLockRecord(vehicleSource.getId(), LockStatus.UNLOCKED.ordinal(), description);
            vehicleLockDaoSupport.save(record);
            
            logger.warn("#syncVehicleByMdm() - vehicle_source[code={}] already unlocked", vehicleSource.getCode());
            
        }
	    
	}
	
	/**
	 * MDM解锁时，锁定FMS已解绑车辆，并对MDM进行调度异常锁定
	 * 
	 * @param transMeans
	 * @param vehicleSource
	 */
	private void lockVehicleByDeleted(RemoteTransMeans transMeans, VehicleSource vehicleSource) {
	    
	    if (!transMeans.isLock()) {
	        
	        String fmsMark = "车辆解绑，调度异常锁定";
	        
	        // 1. lock vehicle_source
	        vehicleLockDao.updateVehicleSource(vehicleSource.doLock(fmsMark, LockType.SHIPMENT_EXCEPTION));
	        
	        // 2. create lock record
	        VehicleSourceLockRecord record = new VehicleSourceLockRecord(vehicleSource.getId(), LockStatus.LOCKED.ordinal(), fmsMark);
	        vehicleLockDaoSupport.save(record);
	        
	        // 3. lock remote_trans_means
	        vehicleLockDao.updateRemoteTransMeans(transMeans.lock(LockType.SHIPMENT_EXCEPTION, Constants.MDM_LOCK_USERID));
	        
	        logger.warn("#lockVehicleByDeleted() - vehicle_source[code={}] already locked", vehicleSource.getCode());
	        
	    }
	    
	}
	
}
